Accurate accounting for credit scheduler
authorKeir Fraser <keir.fraser@citrix.com>
Tue, 25 Aug 2009 14:36:37 +0000 (15:36 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Tue, 25 Aug 2009 14:36:37 +0000 (15:36 +0100)
Rather than debit a full 10ms of credit on a scheduler tick
(probabilistic), debit credits accurately based on time stamps.

The main problem this is meant to address is an attack on the
scheduler that allows a rogue guest to avoid ever being debited
credits.  The basic idea is that the rogue process checks time (using
rdtsc) periodically, and yields after 9.5ms.  Using this technique, a
guest can "steal" 95% of the cpu.  This is particularly an issue in
cloud environments.

Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com>
xen/common/sched_credit.c

index c4e056b35b333d80950ea072921b0ea316d1b70a..a2d79eeb7a9f7a7816d50210a85b2af207301d8d 100644 (file)
 #define CSCHED_MSECS_PER_TICK       10
 #define CSCHED_MSECS_PER_TSLICE     \
     (CSCHED_MSECS_PER_TICK * CSCHED_TICKS_PER_TSLICE)
-#define CSCHED_CREDITS_PER_TICK     100
+#define CSCHED_CREDITS_PER_MSEC    100000
 #define CSCHED_CREDITS_PER_TSLICE   \
-    (CSCHED_CREDITS_PER_TICK * CSCHED_TICKS_PER_TSLICE)
+    (CSCHED_MSECS_PER_TSLICE * CSCHED_CREDITS_PER_MSEC)
 #define CSCHED_CREDITS_PER_ACCT     \
-    (CSCHED_CREDITS_PER_TICK * CSCHED_TICKS_PER_ACCT)
+    (CSCHED_CREDITS_PER_MSEC * CSCHED_MSECS_PER_TSLICE)
+#define CSCHED_STIME_TO_CREDIT(_t)  ((_t)*CSCHED_CREDITS_PER_MSEC/MILLISECS(1))
 
 
 /*
@@ -121,6 +122,7 @@ struct csched_vcpu {
     struct csched_dom *sdom;
     struct vcpu *vcpu;
     atomic_t credit;
+    s_time_t start_time;   /* When we were scheduled (used for credit) */
     uint16_t flags;
     int16_t pri;
 #ifdef CSCHED_STATS
@@ -209,6 +211,24 @@ __runq_remove(struct csched_vcpu *svc)
     list_del_init(&svc->runq_elem);
 }
 
+void burn_credits(struct csched_vcpu *svc, s_time_t now)
+{
+    s_time_t delta;
+
+    /* Assert svc is current */
+    ASSERT(svc==CSCHED_VCPU(per_cpu(schedule_data, svc->vcpu->processor).curr));
+
+    if ( is_idle_vcpu(svc->vcpu) )
+        return;
+
+    delta = (now - svc->start_time);
+
+    if ( delta > 0 ) {
+        atomic_sub(CSCHED_STIME_TO_CREDIT(delta)+1, &svc->credit);
+        svc->start_time = now;
+    } 
+}
+
 static inline void
 __runq_tickle(unsigned int cpu, struct csched_vcpu *new)
 {
@@ -496,7 +516,7 @@ csched_vcpu_acct(unsigned int cpu)
     /*
      * Update credits
      */
-    atomic_sub(CSCHED_CREDITS_PER_TICK, &svc->credit);
+    burn_credits(svc, NOW());
 
     /*
      * Put this VCPU and domain back on the active list if it was
@@ -1116,6 +1136,9 @@ csched_schedule(s_time_t now)
     CSCHED_STAT_CRANK(schedule);
     CSCHED_VCPU_CHECK(current);
 
+    /* Update credits */
+    burn_credits(scurr, now);
+
     /*
      * Select next runnable local VCPU (ie top of local runq)
      */
@@ -1153,6 +1176,9 @@ csched_schedule(s_time_t now)
         cpu_clear(cpu, csched_priv.idlers);
     }
 
+    if ( !is_idle_vcpu(snext->vcpu) )
+        snext->start_time = now;
+
     /*
      * Return task to run next...
      */
@@ -1246,7 +1272,7 @@ csched_dump(void)
            "\trunq_sort          = %u\n"
            "\tdefault-weight     = %d\n"
            "\tmsecs per tick     = %dms\n"
-           "\tcredits per tick   = %d\n"
+           "\tcredits per msec   = %d\n"
            "\tticks per tslice   = %d\n"
            "\tticks per acct     = %d\n"
            "\tmigration delay    = %uus\n",
@@ -1258,7 +1284,7 @@ csched_dump(void)
            csched_priv.runq_sort,
            CSCHED_DEFAULT_WEIGHT,
            CSCHED_MSECS_PER_TICK,
-           CSCHED_CREDITS_PER_TICK,
+           CSCHED_CREDITS_PER_MSEC,
            CSCHED_TICKS_PER_TSLICE,
            CSCHED_TICKS_PER_ACCT,
            vcpu_migration_delay);